%%%-------------------------------------------------------------------
%%% @author Viktor Green <viktor@student.gu.se.se>
%%% @copyright (C) 2011, Viktor Green
%%% @doc
%%% Tests for the tracker_srv.erl module
%%% @end
%%% Created : 13 Dec 2011 by Viktor Green <viktor@student.gu.se>
%%%-------------------------------------------------------------------
-module(tracker_srv_tests).

-include_lib("eunit/include/eunit.hrl").

-include("src/records.hrl").

tracker_srv_test_() ->    
    {ok, Dir} = file:get_cwd(),
    Parent = filename:dirname(Dir),
    Path = filename:join([Parent, priv]),
    {ok, Files} = file:list_dir(Path),
    FixedFiles = lists:map(fun (A) -> filename:join([Path, A]) end, Files),
    {Good, Bad} = create_torrents(FixedFiles, [], []),
    [FirstGood | _] = Good,
    [FirstBad | _ ] = Bad,
    ExitTests = [{timeout, 60, fun() -> test_exit(FirstGood) end},
		 {timeout, 60, fun() -> test_exit(FirstBad) end} | []],
    {generator, fun() -> ExitTests ++ generator(Good, true) ++ generator(Bad, false) end}.

generator([], _) ->
    [];
generator([Head | Tail], Type) ->
    [{timeout, 60, fun() -> assorted_tests(Head, Type) end} | generator(Tail, Type)].

create_torrents([], [Head | Tail], []) ->
    {[Head | Tail], [Head#torrent{id = make_ref(),
				  announce = "bad_name" ++ Head#torrent.announce}]};
create_torrents([], Torrents, Failures) ->
    {Torrents, Failures};
create_torrents([Head | Tail], Torrents, Failures) ->
    case filename:extension(Head) of
	".torrent" ->
	    case string:substr(filename:basename(Head, ".torrent"), 1, 3) of
		"Tes" ->
		    create_torrents(Tail, [create_torrent(Head) | Torrents], Failures);
		"Bad" ->
		    create_torrents(Tail, Torrents, [create_torrent(Head) | Failures]);
		_Else ->
		    create_torrents(Tail, Torrents, Failures)
	    end;
        _ ->
	    create_torrents(Tail, Torrents, Failures)
    end.

create_torrent(Path) ->
    {ok, Parsed} = torrent_file:parse(Path),
    Info = torrent_file:get_value("info", Parsed),
    {string, Tracker} = torrent_file:get_value("announce", Parsed),
    Info_value = bencode:encode_to_binary(Info),
    Info_hash = binary_to_list(crypto:sha(Info_value)),
    {integer, Length} = torrent_file:get_value("length", Info),
    {integer, PLength} = torrent_file:get_value("piece length", Info),
    {string, Pieces} = torrent_file:get_value("pieces", Info),
    #torrent{id = make_ref(), announce = Tracker, info_hash = Info_hash,
	     length = Length, pieceLength = PLength, checkSums = Pieces}.
    

connect(#torrent{announce = Address, info_hash = Info_hash}, PeerID, Port) ->
    {Result, _Result} = tracker:connect(Address, Info_hash, PeerID, Port, 0, 0, 0, "started"),
    ?assert(ok == Result).

assorted_tests(Torrent, Type) ->
    ok = application:start(snoopy_torrent),
    CheckPort = 6996,
    CheckId = "ERLANGTEST0123456789",
    LocalIP = {127,0,0,1},
    LocalAdress = {LocalIP, CheckPort},
    ExternalIP = get_external_ip(),
    ExternalAdress = {ExternalIP, CheckPort},
    InternalIP = get_internal_ip(),
    InternalAdress = {InternalIP, CheckPort},
    case Type of
	true ->
	    connect(Torrent, CheckId, CheckPort);
	_ ->
	    ok
    end,
    timer:sleep(500),
    {Result, Pid} = tracker_srv:start_link(Torrent, 0),
    ?assert(ok == Result),
    timer:sleep(2500),
    ?assert(Type == tracker_srv:is_connected(Pid)),
    ?assert(Type == lists:member(LocalAdress, tracker_srv:get_peers(Pid)) orelse
            Type == lists:member(InternalAdress, tracker_srv:get_peers(Pid)) orelse
            Type == lists:member(ExternalAdress, tracker_srv:get_peers(Pid))),
    Pid ! interval,
    timer:sleep(500),
    Pid ! min_interval,
    timer:sleep(500),
    ok = application:stop(snoopy_torrent).


test_exit(Torrent) ->
    ok = application:start(snoopy_torrent),
    {ok, Pid1} = tracker_srv:start_link(Torrent, 0),
    % Wait for the processes to connect to the trackers
    timer:sleep(2000),
    OldFlag = process_flag(trap_exit, true),
    tracker_srv:stop(Pid1),
    receive
        {'EXIT', Pid1, Reason1} ->
            ?assert(Reason1 == shutdown)
    end,
    process_flag(trap_exit, OldFlag),
    ok = application:stop(snoopy_torrent).


get_external_ip() ->
    inets:start(),
    case httpc:request("http://whatsmyip.net/") of
	{ok, {_Status, Headers, _Body}} ->
	    case lists:keyfind("title", 1, Headers) of
		false ->
		    {127,0,0,1};
		{"title", Title} ->
		    String = string:substr(Title, string:rstr(Title, ":") + 2),
		    inet:getaddr(String, inet)
	    end;
	_Else ->
	    {127,0,0,1}
    end.


get_internal_ip() ->
    case inet:getifaddrs() of
	{ok, List} ->
	    get_internal_ip(List);
	{error, _} ->
	    {127,0,0,1}
    end.

get_internal_ip([]) ->
    {127,0,0,1};
get_internal_ip([{_Name, Options} | Tail]) ->
    {flags, Flags} = lists:keyfind(flags, 1, Options),
    case lists:member(loopback, Flags) of
	true ->
	    get_internal_ip(Tail);
	false ->
	    case lists:member(up, Flags) of
	        false ->
		    get_internal_ip(Tail);
		true ->
		    case lists:member(running, Flags) of
			false ->
			    get_internal_ip(Tail);
			true ->
			    ok
		    end
	    end
    end,
    case get_ip(Options) of
	false ->
	    get_internal_ip(Tail);
	IP -> IP
    end.

get_ip(Options) ->
    case lists:keyfind(addr, 1, Options) of
	false ->
	    false;
	{addr, IP} ->
	    case IP of
		{_, _, _, _} ->
		    IP;
		_ ->
		    get_ip(lists:keydelete(addr, 1, Options))
	    end
    end.
